package nacos

import (
	"errors"
	"fmt"
	"github.com/go-kit/kit/sd"
	"github.com/go-kit/kit/util/conn"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
	"time"

	"github.com/go-kit/log"
	"git.pmx.cn/tq/kit/sd/internal/instance"
)

const defaultIndex = 0

var errStopped = errors.New("quit and closed nacos instancer")

type Instancer struct {
	cache       *instance.Cache
	client      IClient
	logger      log.Logger
	service     string
	metadata    map[string]string
	healthyOnly bool
	quitc       chan struct{}
}

func NewInstance(client IClient, logger log.Logger, service string, metadata map[string]string, healthyOnly bool) *Instancer {
	s := &Instancer{
		cache:       instance.NewCache(),
		client:      client,
		logger:      log.With(logger, "service", service, "tags", fmt.Sprint(metadata)),
		service:     service,
		metadata:    metadata,
		healthyOnly: healthyOnly,
		quitc:       make(chan struct{}),
	}

	// 获取实例
	instances, index, err := s.getInstances(defaultIndex, nil)
	if err == nil {
		s.logger.Log("instances", len(instances))
	} else {
		s.logger.Log("err", err)
	}

	s.cache.Update(sd.Event{Instances: instances, Err: err})
	go s.loop(index)
	return s

}

// Stop terminates the instancer.
func (s *Instancer) Stop() {
	close(s.quitc)
}

func (s *Instancer) loop(lastIndex uint64) {
	var (
		instances []string
		err       error
		d         time.Duration = 10 * time.Millisecond
		index     uint64
	)
	for {
		instances, index, err = s.getInstances(lastIndex, s.quitc)
		switch {
		case errors.Is(err, errStopped):
			return // stopped via quitc
		case err != nil:
			s.logger.Log("err", err)
			time.Sleep(d)
			d = conn.Exponential(d)
			s.cache.Update(sd.Event{Err: err})
		case index == defaultIndex:
			s.logger.Log("err", "index is not sane")
			time.Sleep(d)
			d = conn.Exponential(d)
		case index < lastIndex:
			s.logger.Log("err", "index is less than previous; resetting to default")
			lastIndex = defaultIndex
			time.Sleep(d)
			d = conn.Exponential(d)
		default:
			lastIndex = index
			s.cache.Update(sd.Event{Instances: instances})
			d = 10 * time.Millisecond
		}
	}

}

// Register implements Instancer.
func (s *Instancer) Register(ch chan<- sd.Event) {
	s.cache.Register(ch)
}

// Deregister implements Instancer.
func (s *Instancer) Deregister(ch chan<- sd.Event) {
	s.cache.Deregister(ch)
}


// getInstances
// TODO: 未完成
func (s *Instancer) getInstances(lastIndex uint64, interruptc chan struct{}) ([]string, uint64, error) {

	type response struct {
		instances []string
		index     uint64
	}

	var (
		errc = make(chan error, 1)
		resc = make(chan response, 1)
	)
	cluster := make([]string, 0)

	getInstancesParam := vo.SelectInstancesParam{
		ServiceName: "auth",
		GroupName: "api",
		Clusters: cluster,
		HealthyOnly: true,
	}


	go func() {
		entries, err := s.client.GetInstances(getInstancesParam)
		if err != nil {
			errc <- err
			return
		}

		fmt.Printf("%+v\n ", entries)


	}()

	select {
	case err := <-errc:
		return nil, 0, err
	case res := <-resc:
		return res.instances, res.index, nil
	case <-interruptc:
		return nil, 0, errStopped
	}

}
